﻿using System;
using MailClient.Commands;
using MailClient.Handlers;

namespace MailClient.Data
{
    /// <summary>
    /// determines which system command to use
    /// </summary>
    class command : reply
    {
        private designator _flag;
        public designator flag { get { return _flag; } }


        public Command Parse(string query)
        {
            query = query.Trim();
            switch (query)
            {
                case "contact":
                case "contacts":
                case "Contacts":
                case "Contact":
                    return Command.Contacts;
                case "Help":
                case "help":
                    return Command.Help;
                case "info":
                case "Info":
                    return Command.Info;
                case "list":
                case "List":
                    return Command.List;
                case "approval":
                case "Approval":
                    return Command.Approval;
                case "stop":
                case "Stop":
                    return Command.Stop;
                case "subscribe":
                case "Subscribe":
                    return Command.Subscribe;
                case "training":
                case "Training":
                    return Command.Training;
                case "unsubscribe":
                case "Unsubscribe":
                case "unsub":
                case "Unsub":
                    return Command.Unsubscribe;
                case "request":
                case "Request":
                    return Command.Request;
                default:
                    return Command.None;
            }
        }

        public Command[] Parse(string[] query)
        {
            Command[] arg = { };
            int i = 0;
            foreach (string s in query)
            {
                try
                {
                    Command thiscommand = (Command)Command.Parse(typeof(Command), s, true);
                    arg[i] = thiscommand;
                    i++;
                    break;
                }
                catch (ArgumentNullException enull)
                {
                    arg[i] = Command.None;
                    i++;
                    break;
                }
                catch (ArgumentException ex)
                {
                    arg[i] = Command.None;
                    i++;
                    break;
                }
                catch (OverflowException eover)
                {
                    arg[i] = Command.None;
                    i++;
                    break;
                }
            }
            return arg;
        }

        public Commands.ICommand TryParse(ref string query)
        {
            /* I'm expecting commands to follow this format
             * Command query Argument
             * The only command which won't follow this is the Help Command
             * I do have a special case with the Approval command since it implements a "-" prefix
             * 
             * query can be singular or a comma separated list.  The list can be any combination of values
             * Contact Names, Jobs, Store numbers, job types
             */
            Command com1;
            Command com2;
            query = RemoveReply(query);
            TryParse(query, out com1, out com2);

            string[] list;
            if (com1 == Command.None)
            {
                list = query.Split(new char[] { ',' });
            }
            else if (com2 != Command.None)
            {
                /* // we need to remove trailing commas*/
                // adding a +1 to the lengths becuase there should be a space
                // character between the two commands
                query = query.Remove(0, (com1.ToString().Length + com2.ToString().Length + 1));
                list = query.Split(new char[] { ',' });
            }
            else
            {
                query = query.Remove(0, com1.ToString().Length);
                list = query.Split(new char[] { ',' });
            }

            //Step Three parse the first word for a command and pass the remainder of the string back for further processing

            {
                switch (com1)
                {
                    // the Word extension takes a zero based count for the start position.  for now
                    // the length of commands will be hard coded since we do know how long those are.

                    case Command.Approval:
                        using (approval ap = new approval(com2, list))
                        {
                            ap.Designator = _flag;
                            return Cast(ap);
                        }
                    case Command.Help:
                        help hp = new help(com2);
                        return Cast(hp);
                    case Command.Info:
                        info io = new info(com2, list);
                        return Cast(io);
                    case Command.List:
                        list li = new list(com2, list);
                        return Cast(li);
                    case Command.Stop:
                        stop st = new stop(com2, list);
                        return Cast(st);
                    case Command.Contacts:
                        contact co = new contact(com2, list);
                        return Cast(co);
                    case Command.Subscribe:
                        subscribe su = new subscribe(com2, list);
                        return Cast(su);
                    case Command.Unsubscribe:
                        unsubscribe un = new unsubscribe(com2, list);
                        return Cast(un);
                    case Command.Training:
                        Training tr = new Training(list);
                        return Cast(tr);
                    default:
                        none no = new none(list);
                        return Cast(no);
                }
            }
        }

        private Commands.ICommand Cast(object ob)
        {
            Commands.ICommand c = (Commands.ICommand)ob;
            return c;
        }

        private void TryParse(string query, out Command com1, out Command com2)
        {
            // Step One , get the first element then test if it is the special flag "-"
            //String test = temp_query[0];
            char t = query[0];
            if (t.ToString() == "-")
            {
                _flag = designator.Remove;
                query.TrimStart(t);
            }
            else
            { _flag = designator.Add; }

            // Step Two.  Get the first word and test it for a command
            com1 = Parse(query.Word(0, " "));

            // Step Three try to parse the second word
            com2 = Parse(query.Word(com1.ToString().Length + 1, " "));


        }

        protected Commands.ICommand Parse(DeliverableTypes type)
        {
            switch (type)
            {
                // the Word extension takes a zero based count for the start position.  for now
                // the length of commands will be hard coded since we do know how long those are.

                case DeliverableTypes.Merch:
                    using (Merch com = new Merch(Command.None, new string[] { }))
                    {
                        com.Designator = _flag;
                        return Cast(com);
                    }
                default:
                    using (none com = new none(new string[] { }))
                    {
                        return Cast(com);
                    }
            }
        }

        /// <summary>
        /// Prints all enum combinations to the console
        /// </summary>
        public void Print_Command_combinations()
        {
            Console.WriteLine(
                  "\nAll possible combinations of enum \n" +
                  "Command :\n");
            for (int val = 0; val <= 128; val++)
                Console.WriteLine("{0, 3} - {1}", val, ((Command)val).ToString());
            Console.ReadLine();
        }
    }

}